package com.crane.framework.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.StrUtil;

import com.alibaba.fastjson.JSON;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.crane.framework.annotation.TransferFile;
import com.crane.framework.constant.SystemConst;
import com.crane.framework.entity.Config;
import com.crane.framework.entity.File;
import com.crane.framework.enums.PictureEnum;
import com.crane.framework.exception.RRException;
import com.crane.framework.service.IConfigService;
import com.crane.framework.service.IFileService;
import com.crane.framework.mapper.FileMapper;
import com.crane.framework.utils.CommonUtil;

import com.crane.framework.utils.SystemUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import org.dromara.x.file.storage.core.FileInfo;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import static com.crane.framework.utils.DateFormatUtil.convertToLocalDateTimeToDate;

/**
 * @author 47349
 * @description 针对表【sys_file(文件记录表)】的数据库操作Service实现
 * @createDate 2024-01-18 04:32:48
 */
@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, File> implements IFileService {
  @Resource private IConfigService configService;

  /** 保存文件信息到数据库 */
  @Override
  public boolean save(FileInfo info) {
    File detail =
        BeanUtil.copyProperties(
            info, File.class, "metadata", "userMetadata", "thMetadata", "thUserMetadata", "attr");

    // 这是手动获 元数据 并转成 json 字符串，方便存储在数据库中
    detail.setMetadata(valueToJson(info.getMetadata()));
    detail.setUserMetadata(valueToJson(info.getUserMetadata()));
    detail.setThMetadata(valueToJson(info.getThMetadata()));
    detail.setThUserMetadata(valueToJson(info.getThUserMetadata()));
    // 这是手动获 取附加属性字典 并转成 json 字符串，方便存储在数据库中
    detail.setAttr(valueToJson(info.getAttr()));
    boolean b = save(detail);
    if (b) {
      info.setId(detail.getId());
    }
    return b;
  }

  /** 根据 url 查询文件信息 */
  @Override
  public FileInfo getByUrl(String url) {
    File detail = this.lambdaQuery().eq(File::getUrl, url).one();
    FileInfo info =
        BeanUtil.copyProperties(
            detail,
            FileInfo.class,
            "metadata",
            "userMetadata",
            "thMetadata",
            "thUserMetadata",
            "attr");

    // 这是手动获取数据库中的 json 字符串 并转成 元数据，方便使用
    info.setMetadata(jsonToMetadata(detail.getMetadata()));
    info.setUserMetadata(jsonToMetadata(detail.getUserMetadata()));
    info.setThMetadata(jsonToMetadata(detail.getThMetadata()));
    info.setThUserMetadata(jsonToMetadata(detail.getThUserMetadata()));
    // 这是手动获取数据库中的 json 字符串 并转成 附加属性字典，方便使用
    //    info.setAttr(jsonToDict(detail.getAttr()));
    return info;
  }

  /** 根据 url 删除文件信息 */
  @Override
  public boolean delete(String url) {
    File file = this.lambdaQuery().eq(File::getUrl, url).one();
    if (CommonUtil.isNullOrEmpty(file)) return false;
    this.removeById(file.getId());
    return true;
  }

  @Override
  public FileInfo getByIdItem(String id) throws JsonProcessingException {
    File detail = this.lambdaQuery().eq(File::getId, id).one();
    if (CommonUtil.isNullOrEmpty(detail)) {
      throw new RRException("文件可能已经被删除，请重新上传");
    }
    FileInfo info = SystemUtil.buildResource(detail, FileInfo.class);

    info.setCreateTime(convertToLocalDateTimeToDate(detail.getGmtCreate()));

    // 这是手动获取数据库中的 json 字符串 并转成 附加属性字典，方便使用
    if (StrUtil.isNotBlank(detail.getAttr())) {
      info.setAttr(new ObjectMapper().readValue(detail.getAttr(), Dict.class));
    }
    return info;
  }

  /** 将指定值转换成 json 字符串 */
  public String valueToJson(Object value) {
    if (value == null) return null;
    // 通过fastjson
    return JSON.toJSONString(value);
  }

  /** 将 JSON 字符串转换成元数据对象 */
  public Map<String, String> jsonToMetadata(String json) {
    if (StrUtil.isBlank(json)) return null;
    return JSON.parseObject(json, new TypeReference<Map<String, String>>() {});
  }

  @Override
  public <E> void transfer(E e) {
    List<Config> configList = configService.getPreviewConfig();
    if (CommonUtil.isNullOrEmpty(configList)) {
      return;
    }

    if (e instanceof Collection) {
      ((Collection<?>) e)
          .forEach(
              i -> {
                Class<?> aClass = i.getClass();
                Field[] fieldItems = aClass.getDeclaredFields();
                if (CommonUtil.notNullOrEmpty(fieldItems)) {
                  for (Field fieldItem : fieldItems) {
                    TransferFile annotation = fieldItem.getAnnotation(TransferFile.class);
                    if (CommonUtil.isNullOrEmpty(annotation)) {
                      continue;
                    }
                    String field = annotation.field();
                    PictureEnum type = annotation.type();
                    StringBuilder baseUrl = new StringBuilder();
                    if (type == PictureEnum.THUMBNAIL) {
                      baseUrl.append(
                          configList.stream()
                              .filter(
                                  item ->
                                      item.getConfigName()
                                          .equals(
                                              SystemUtil.isWin()
                                                  ? SystemConst.WinThumbnailPreview
                                                  : SystemConst.LinuxThumbnailPreview))
                              .findFirst()
                              .orElse(new Config())
                              .getConfigValue());
                    } else {
                      baseUrl.append(
                          configList.stream()
                              .filter(
                                  item ->
                                      item.getConfigName()
                                          .equals(
                                              SystemUtil.isWin()
                                                  ? SystemConst.WinPreview
                                                  : SystemConst.LinuxPreview))
                              .findFirst()
                              .orElse(new Config())
                              .getConfigValue());
                    }
                    try {
                      Field f = aClass.getDeclaredField(field);
                      f.setAccessible(true);
                      Object o = f.get(i);
                      if (o instanceof String[] ss) {
                        fieldItem.setAccessible(true);
                        List<String> objects = new ArrayList<>();
                        for (String s : ss) {
                          if (CommonUtil.isNullOrEmpty(s)) {
                            continue;
                          }
                          String s1 = baseUrl.toString();
                          String a = s1 + s;
                          objects.add(a);
                        }
                        String[] applyIdStr = objects.toArray(new String[0]);
                        fieldItem.set(i, applyIdStr);
                      } else if (o instanceof List<?> list) {
                        fieldItem.setAccessible(true);
                        List<String> objects = new ArrayList<>();
                        for (Object s : list) {
                          if (CommonUtil.isNullOrEmpty(s)) {
                            continue;
                          }
                          String s1 = baseUrl.toString();
                          String a = s1 + s;
                          objects.add(a);
                        }
                        fieldItem.set(i, objects);
                      } else {
                        String s = (String) o;
                        if (CommonUtil.isNullOrEmpty(s)) {
                          continue;
                        }
                        fieldItem.setAccessible(true);
                        fieldItem.set(i, baseUrl.append(s).toString());
                      }
                    } catch (Exception ex) {
                      log.error("图片插入失败", ex);
                    }
                  }
                }
              });
    } else {
      Class<?> aClass = e.getClass();
      Field[] fieldItems = aClass.getDeclaredFields();
      if (CommonUtil.notNullOrEmpty(fieldItems)) {
        for (Field fieldItem : fieldItems) {
          TransferFile annotation = fieldItem.getAnnotation(TransferFile.class);
          if (CommonUtil.isNullOrEmpty(annotation)) {
            continue;
          }
          String field = annotation.field();
          PictureEnum type = annotation.type();
          try {
            Field f = aClass.getDeclaredField(field);
            f.setAccessible(true);
            Object o = f.get(e);
            if (o instanceof String[] ss) {
              StringBuilder baseUrl = new StringBuilder();
              if (type == PictureEnum.THUMBNAIL) {
                baseUrl.append(
                    configList.stream()
                        .filter(
                            item ->
                                item.getConfigName()
                                    .equals(
                                        SystemUtil.isWin()
                                            ? SystemConst.WinThumbnailPreview
                                            : SystemConst.LinuxThumbnailPreview))
                        .findFirst()
                        .orElse(new Config())
                        .getConfigValue());
              } else {
                baseUrl.append(
                    configList.stream()
                        .filter(
                            item ->
                                item.getConfigName()
                                    .equals(
                                        SystemUtil.isWin()
                                            ? SystemConst.WinPreview
                                            : SystemConst.LinuxPreview))
                        .findFirst()
                        .orElse(new Config())
                        .getConfigValue());
              }
              fieldItem.setAccessible(true);
              List<String> objects = new ArrayList<>();
              for (String s : ss) {
                if (CommonUtil.isNullOrEmpty(s)) {
                  continue;
                }
                String s1 = baseUrl.toString();
                String a = s1 + s;
                objects.add(a);
              }
              String[] applyIdStr = objects.toArray(new String[0]);
              fieldItem.set(e, applyIdStr);
            } else if (o instanceof List<?> list) {
              StringBuilder baseUrl = new StringBuilder();
              if (type == PictureEnum.THUMBNAIL) {
                baseUrl.append(
                    configList.stream()
                        .filter(
                            item ->
                                item.getConfigName()
                                    .equals(
                                        SystemUtil.isWin()
                                            ? SystemConst.WinThumbnailPreview
                                            : SystemConst.LinuxThumbnailPreview))
                        .findFirst()
                        .orElse(new Config())
                        .getConfigValue());
              } else {
                baseUrl.append(
                    configList.stream()
                        .filter(
                            item ->
                                item.getConfigName()
                                    .equals(
                                        SystemUtil.isWin()
                                            ? SystemConst.WinPreview
                                            : SystemConst.LinuxPreview))
                        .findFirst()
                        .orElse(new Config())
                        .getConfigValue());
              }
              fieldItem.setAccessible(true);
              List<String> objects = new ArrayList<>();
              for (Object s : list) {
                if (CommonUtil.isNullOrEmpty(s)) {
                  continue;
                }
                String s1 = baseUrl.toString();
                String a = s1 + s;
                objects.add(a);
              }
              fieldItem.set(e, objects);
            } else {
              String s = (String) f.get(e);
              if (CommonUtil.isNullOrEmpty(s)) {
                continue;
              }

              StringBuilder baseUrl = new StringBuilder();

              if (type == PictureEnum.THUMBNAIL) {
                baseUrl.append(
                    configList.stream()
                        .filter(
                            item ->
                                item.getConfigName()
                                    .equals(
                                        SystemUtil.isWin()
                                            ? SystemConst.WinThumbnailPreview
                                            : SystemConst.LinuxThumbnailPreview))
                        .findFirst()
                        .orElse(new Config())
                        .getConfigValue());
              } else {
                baseUrl.append(
                    configList.stream()
                        .filter(
                            item ->
                                item.getConfigName()
                                    .equals(
                                        SystemUtil.isWin()
                                            ? SystemConst.WinPreview
                                            : SystemConst.LinuxPreview))
                        .findFirst()
                        .orElse(new Config())
                        .getConfigValue());
              }

              fieldItem.setAccessible(true);
              fieldItem.set(e, baseUrl.append(s).toString());
            }
          } catch (Exception ex) {
            log.error("图片插入失败", ex);
          }
        }
      }
    }
  }

  @Override
  public <E> void transfer(Page<E> e) {
    transfer(e.getRecords());
  }
}
